/**
 * @file STL_Scheduler.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2021-03-04
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2021
 * 
 */

#include "STL_Scheduler.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "STLOG.h"

#ifdef STL_THREAD_CHECK
#include "STL_ThreadCheck.h"
#endif

namespace STL
{

    STL_Scheduler *STL_Scheduler::m_instance = NULL;

    STL_Scheduler *STL_Scheduler::getInstance()
    {
        if (!m_instance)
            m_instance = new STL_Scheduler();
        return m_instance;
    }

    void STL_Scheduler::destroy()
    {
        if (m_instance)
        {
            delete m_instance;
            m_instance = NULL;
        }
    }

    void STL_Scheduler::setInterval(STL_SchedulerCallback cb, float interval, std::string key)
    {
        schedule(cb, interval, -1, 0.001f, false, key);
    }

    void STL_Scheduler::setTimeout(STL_SchedulerCallback cb, float delay, std::string key)
    {
        schedule(cb, 0.0f, 1, delay, false, key);
    }

    void STL_Scheduler::schedule(STL_SchedulerCallback cb, float interval, unsigned int repeat, float delay, bool paused, std::string key)
    {
        STL_SchedulerObject *obj = new STL_SchedulerObject(cb, delay, paused, interval, repeat);
        std::lock_guard<std::mutex> guard(m_mutex);
        m_callbacks.insert(std::pair<std::string, STL_SchedulerObject *>(key, obj));
    }

    void STL_Scheduler::unschedule(std::string key)
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        if (m_callbacks[key])
        {
            m_callbacks[key]->setShouldDeleted(true);
        }
    }

    STL_Scheduler::STL_Scheduler()
    {
        m_isRunning = true;
        m_callbacks.clear();
        //  m_thread=new std::thread(&STL_Scheduler::loop,this);
    }

    STL_Scheduler::~STL_Scheduler()
    {
        m_isRunning = false;
        //  m_thread->join();
        //  delete m_thread;
        //  unscheduleAll();
    }

    void STL_Scheduler::run()
    {
        updateSchedule();
        std::map<std::string, STL_SchedulerObject *> callbacks;
        {
            std::lock_guard<std::mutex> guard(m_mutex);
            callbacks = m_callbacks;
        }

        for (std::map<std::string, STL_SchedulerObject *>::iterator it = callbacks.begin(); it != callbacks.end(); it++)
        {
            // stlog_info("check key:%s",it->first.c_str());
            //    timespec currentTime;
            //    clock_gettime(CLOCK_REALTIME,&currentTime);
            struct timeval currentTime;
            struct timezone tz;
            gettimeofday(&currentTime, &tz);
            struct timeval lastTime = it->second->getLastTime();
            float delay = it->second->getDelay();
            float interval = it->second->getInterval();
            float callInterval = currentTime.tv_sec - lastTime.tv_sec + ((float)(currentTime.tv_usec - lastTime.tv_usec)) / 1000000;
            if (callInterval < 0) // 系统时间错乱导致时间未负数
            {
                it->second->setLastTime(currentTime); // 重置任务时间
                continue;
            }
            if (it->second->getPaused()) // 判断是否暂停
            {
                continue; // 调度器任务暂停中,跳过
            }
            if (delay > 0) // 进入计算延迟逻辑
            {
                if (callInterval < delay)
                {
                    continue; // 调度器任务延迟时间未到,跳过
                }
                else
                {
                    it->second->setDelay(0); // 调度器任务延迟时间到达,延迟时间置0,不再计算延迟
                    callInterval = interval; // 延迟完成,通过设置调度间隔,直接出发执行。
                    // TODO 延迟时间完成,应该做第一次执行才对
                }
            }
            if (it->second->getRepeat() == 0) // 判断执行次数
            {
                it->second->setShouldDeleted(true); // 标记删除
                // stlog_debug("STL_Scheduler::run() setShouldDeleted\n");
                continue; // 调度器任务完成指定执行次数,跳过
            }
            else
            {
                if (callInterval < interval) // 判断是否到达执行时间
                {
                    continue; // 未到达执行时间,跳过
                }
            }
            if (callInterval > 1000) // 判断是否大于1000秒
            {
                callInterval = interval; // 大于1000秒修改为间隔时间,// TODO 在干嘛,掩盖调度精度问题么？
            }
            // stlog_info("run key:%s",it->first.c_str());
#ifdef STL_THREAD_CHECK
            STL_ThreadCheck::getInstance()->SetCheckTaskDescribe(std::string("STL_Scheduler:") + it->first);
#endif
            it->second->onUpdate(callInterval); // 执行调度任务
#ifdef STL_THREAD_CHECK
            STL_ThreadCheck::getInstance()->SetCheckTaskDescribe(std::string("STL_Scheduler: IDLE"));
#endif
            if (it->second->getRepeat() > 0) // 判断是否有调用计数
            {
                it->second->setRepeat(it->second->getRepeat() - 1); // 调用计数减1
            }
            it->second->setLastTime(currentTime); // 记录最后触发时间
        }
        usleep(100000);
    }

    void STL_Scheduler::unscheduleAll()
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        for (std::map<std::string, STL_SchedulerObject *>::iterator it = m_callbacks.begin(); it != m_callbacks.end(); it++)
        {
            it->second->setShouldDeleted(false);
        }
    }

    void STL_Scheduler::updateSchedule()
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        //std::cout<<"begin updateSchedule"<<std::endl;
        for (std::map<std::string, STL_SchedulerObject *>::iterator it = m_callbacks.begin(); it != m_callbacks.end();)
        {
            if (it->second->getShouldDeleted())
            {
                delete it->second;
                m_callbacks.erase(it++);
            }
            else
            {
                it++;
            }
        }
        //std::cout<<"end updateSchedule"<<std::endl;
    }

    // static int watchdog_fd = 0;

    // void STL_Scheduler::startWatchdog()
    // {
    //     int interval;
    //     int bootstatus;

    //     if (!watchdog_fd)
    //     {
    //         interval = 20;
    //         watchdog_fd = open(WATCHDOGDEV, O_RDWR);
    //         ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &interval);
    //         ioctl(watchdog_fd, WDIOC_GETTIMEOUT, &interval);
    //         log("Watchdog interval set to %d\n", interval);
    //         //        if (ioctl(watchdog_fd, WDIOC_GETBOOTSTATUS, &bootstatus) == 0) {
    //         //            log("Last boot is caused by : %s\n",
    //         //                (bootstatus != 0) ? "Watchdog" : "Power-On-Reset");
    //         //            if (bootstatus != 0) {
    //         //                AccountData::getInstance()->setFirstRun(false);
    //         //            }else {
    //         //                AccountData::getInstance()->setFirstRun(true);
    //         //            }
    //         //        } else {
    //         //            log("Error: Cannot read watchdog status");
    //         //        }
    //     }
    // }

    // void STL_Scheduler::stopWatchdog()
    // {
    //     if (watchdog_fd)
    //     {
    //         write(watchdog_fd, "V", 1);

    //         close(watchdog_fd);

    //         watchdog_fd = -1;
    //     }
    // }

    // void STL_Scheduler::updateWatchdog()
    // {
    //     if (watchdog_fd)
    //     {
    //         //        log("updateWatchdog1");
    //         ioctl(watchdog_fd, WDIOC_KEEPALIVE, NULL);
    //         //        log("updateWatchdog2");
    //     }
    // }

    void STL_Scheduler::loop()
    {
        while (m_isRunning)
        {
            run();
        }
    }

}